package uz.rt.linac;

/**
 * A control point defines the parameters of the linac at a specific moment during treatment:
 * <ul>
 * <li><b>Linac state:</b> defines the state of:
 * <ul>
 * <li>energy (MV / MeV)</li>
 * <li>gantry, collimator and table angle</li>
 * <li>position of all the leaves and jaws</li>
 * </ul>
 * </li>
 * <li><b>Cumulative Monitor Unit</b> count</li>
 * </ul>
 *
 * @author Wacko von Belsebub
 * @version 1.0.0
 * @see Linac
 */
public class ControlPoint implements Comparable
{
// ------------------------------ FIELDS ------------------------------

    /**
     * State of the linac and all it's subcomponents at a specific moment during treatment.
     */
    protected Linac state = null;
    /**
     * Cumulative Monitor Unit count defines the total amount of monitor units given at before this state.
     */
    protected double cmu;
    /**
     * Defines if a wegde is used in this control point
     */
    protected boolean wegdeUsed = false;
    /**
     * When set to true, a tray should connected to the linac
     */
    protected boolean trayUsed = false;

// --------------------------- CONSTRUCTORS ---------------------------

    /**
     * Constructor initialises the Cumulative Monitor Unit count to 0.
     */
    public ControlPoint()
    {
        cmu = 0.0;
    }

    /**
     * Constructor initialises the state of the linac and sets the Cumulative Monitor Unit count to 0.
     *
     * @param l state of the linac
     */
    public ControlPoint(final Linac l)
    {
        state = l;
        cmu = 0.0;
    }

// --------------------- GETTER / SETTER METHODS ---------------------

    /**
     * Get the Cumulative Monitor Unit count
     *
     * @return Cumulative Monitor Unit count
     */
    public double getCmu()
    {
        return cmu;
    }

    /**
     * Get the current state of the linac
     *
     * @return state of the linac
     */
    public Linac getState()
    {
        return state;
    }

    /**
     * Set the current state of the linac
     *
     * @param state state of the linac
     */
    public void setState(Linac state)
    {
        this.state = state;
    }

    /**
     * Returns if a tray is used in this control point
     *
     * @return True if a wedge is tray, false if not
     */
    public boolean isTrayUsed()
    {
        return trayUsed;
    }

    /**
     * Returns if a wedge is used in this control point
     *
     * @return True if a wedge is used, false if not
     */
    public boolean isWegdeUsed()
    {
        return wegdeUsed;
    }

// ------------------------ CANONICAL METHODS ------------------------

    @Override
    public boolean equals(Object o)
    {
        if (this == o) return true;
        if (!(o instanceof ControlPoint)) return false;

        ControlPoint that = (ControlPoint) o;

        return Double.compare(that.cmu,
                              cmu) == 0 && trayUsed == that.trayUsed && wegdeUsed == that.wegdeUsed && state.equals(
                that.state);

    }

    @Override
    public int hashCode()
    {
        int result;
        long temp;
        result = state.hashCode();
        temp = cmu != +0.0d ? Double.doubleToLongBits(cmu) : 0L;
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        result = 31 * result + (wegdeUsed ? 1 : 0);
        result = 31 * result + (trayUsed ? 1 : 0);
        return result;
    }

// ------------------------ INTERFACE METHODS ------------------------


// --------------------- Interface Comparable ---------------------

    public int compareTo(Object o)
    {
        if (!(o instanceof ControlPoint)) throw new ClassCastException("Expected ControlPoint object");
        ControlPoint cp = (ControlPoint) o;
        return Double.compare(cmu, cp.cmu);
    }

// -------------------------- OTHER METHODS --------------------------

    /**
     * Set the Cumulative Monitor Unit count
     *
     * @param cmu Cumulative Monitor Unit count
     */
    public void setCmu(double cmu)
    {
        if (cmu >= 0.0)
            this.cmu = cmu;
        else throw new RuntimeException("Cumulative monitor unit value must be larger then or equal to 0;");
    }

    /**
     * Set if a tray is used in this control point. Only possible if the linac contains a mount for a tray.
     *
     * @param trayUsed True if a tray is used, false if not
     */
    public void setTrayUsed(boolean trayUsed)
    {
        this.trayUsed = state.isTrayPresent() && trayUsed;
    }

    /**
     * Set if a wedge is used in this control point. Only possible if the linac contains a wedge.
     *
     * @param wegdeUsed True if a wedge is used, false if not
     */
    public void setWegdeUsed(boolean wegdeUsed)
    {
        this.wegdeUsed = state.isWedgePresent() && wegdeUsed;
    }
}
